package weeny.pre.face;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
import java.lang.reflect.Field;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import prefuse.Visualization;
import prefuse.render.AbstractShapeRenderer;
import prefuse.render.AxisRenderer;
import prefuse.render.EdgeRenderer;
import prefuse.render.LabelRenderer;
import prefuse.render.PolygonRenderer;
import prefuse.render.Renderer;
import prefuse.render.ShapeRenderer;
import prefuse.util.PredicateChain;
import prefuse.visual.expression.InGroupPredicate;
import weeny.face.LabeledBox;
import weeny.pre.util.Pre;
import weeny.util.logs.Debug;

/**
 * Allow users change parameters of renderers in the interface. To use it, just
 * add this box to some UI component. To add your own setting box, implement the
 * {@link Setting} interface, and add it to {@link #settingMap}. We choose to
 * show them based if your renderer can be casted to the classes stored in the
 * {@link #settingMap}. We have implemented several {@link Setting}s:
 * {@link AbstractShapeRenderer}, {@link AxisRenderer}, {@link EdgeRenderer},
 * {@link LabelRenderer}, {@link ShapeRenderer} and {@link PolygonRenderer}.
 * 
 * @author weeny
 */
public class RendererBox extends Box{
	
	private final String NO_RENDERER = "<none>";
	
	public static interface Setting{
		public static final String[] ALIGNMENT_OPTS = new String[]{
			"LEFT", "RIGHT", "CENTER", "BOTTOM", "TOP",
			"FAR LEFT", "FAR RIGHT", "FAR BOTTOM", "FAR TOP"
		};
		public Box getSettingBox(Visualization vis, Renderer ren);
	}
	public static void visRepaint(Visualization vis){
		if(vis!=null){
			vis.invalidateAll();
			vis.repaint();
		}
	}
	public static Object fieldObj(String name, Object obj){
		try{
			Field field = obj.getClass().getDeclaredField(name);
			field.setAccessible(true);
			return field.get(obj);
		}
		catch (Exception ex) {
			ex.printStackTrace();
		}
		return null;
	}
	public static class AbstractShapeSetting implements Setting{
		Visualization vis = null;
		AbstractShapeRenderer asr = null;
		JComboBox combo = new JComboBox();{
			combo.addItem("NONE");
			combo.addItem("DRAW");
			combo.addItem("FILL");
			combo.addItem("DRAW AND FILL");
			combo.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					if (asr != null) {
						asr.setRenderType(combo.getSelectedIndex());
						visRepaint(vis);
					}
				}
			});
		}
		LabeledBox box = new LabeledBox("Abstract Shape");{
			box.addComp("Render type:", combo);
		}
		public Box getSettingBox(Visualization vis, Renderer ren) {
			this.vis = null;
			this.asr = null;
			AbstractShapeRenderer tr = (AbstractShapeRenderer) ren;
			combo.setSelectedIndex(tr.getRenderType(null));
			this.vis = vis;
			this.asr = (AbstractShapeRenderer) ren;
			return box;
		}
	}
	public static class ShapeSetting implements Setting{
		Visualization vis = null;
		ShapeRenderer sr = null;
		JSpinner size = new JSpinner();{
			size.addChangeListener(new ChangeListener() {
				public void stateChanged(ChangeEvent arg0) {
					if (sr != null) {
						sr.setBaseSize(((Number)size.getValue()).intValue());
						visRepaint(vis);
					}
				}
			});
		}
		LabeledBox box = new LabeledBox("Shape");{
			box.addComp("Default size:", size);
		}
		public Box getSettingBox(Visualization vis, Renderer ren) {
			this.vis = null;
			this.sr = null;
			ShapeRenderer tr = (ShapeRenderer) ren;
			size.setValue(tr.getBaseSize());
			this.vis = vis;
			this.sr = (ShapeRenderer) ren;
			return box;
		}
	}
	public static class PolygonSetting implements Setting{
		Visualization vis = null;
		PolygonRenderer pr = null;
		JComboBox typeCombo = new JComboBox();{
			typeCombo.addItem("LINE");
			typeCombo.addItem("CURVE");
			typeCombo.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					if (pr != null) {
						pr.setPolyType(typeCombo.getSelectedIndex());
						visRepaint(vis);
					}
				}
			});
		}
		JCheckBox closeCheck = new JCheckBox("");{
			closeCheck.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					if (pr != null) {
						pr.setClosePath(closeCheck.isSelected());
						visRepaint(vis);
					}
				}
			});
		}
		JFormattedTextField sFld = decimalField();{
			sFld.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					if (pr != null) {
						pr.setCurveSlack(Float.parseFloat(sFld.getText()));
						visRepaint(vis);
					}
				}
			});
		}
		LabeledBox box = new LabeledBox("Polygon");{
			box.addComp("Curve type:", typeCombo);
			box.addComp("Curve slack:", sFld);
			box.addComp("Close path:", closeCheck);
		}
		public Box getSettingBox(Visualization vis, Renderer ren) {
			this.vis = null;
			this.pr = null;
			PolygonRenderer tr = (PolygonRenderer) ren;
			typeCombo.setSelectedIndex(tr.getPolyType());
			sFld.setText(tr.getCurveSlack()+"");
			closeCheck.setSelected(tr.isClosePath());
			this.pr = (PolygonRenderer) ren;
			this.vis = vis;
			return box;
		}
	}
	public static class AxisSetting implements Setting{
		Visualization vis = null;
		AxisRenderer ar = null;
		JComboBox xCombo = new JComboBox(ALIGNMENT_OPTS);{
			xCombo.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					if (ar != null) {
						ar.setHorizontalAlignment(xCombo.getSelectedIndex());
						visRepaint(vis);
					}
				}
			});
		}
		JComboBox yCombo = new JComboBox(ALIGNMENT_OPTS);{
			yCombo.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					if (ar != null) {
						ar.setVerticalAlignment(yCombo.getSelectedIndex());
						vis.repaint();
					}
				}
			});
		}
		LabeledBox box = new LabeledBox();{
			box.addComp("X alignment:", xCombo);
			box.addComp("Y alignment:", yCombo);
		}
		public Box getSettingBox(Visualization vis, Renderer ren) {
			this.vis = null;
			ar = null;
			Object xali = fieldObj("m_xalign", ren);
			Object yali = fieldObj("m_yalign", ren);
			if (xali != null) {
				xCombo.setSelectedIndex((Integer)xali);
			}
			if (yali != null) {
				yCombo.setSelectedIndex((Integer)yali);
			}
			this.vis = vis;
			this.ar = (AxisRenderer) ren;
			return box;
		}
	}
	public static class EdgeSetting implements Setting{
		Visualization vis;
		EdgeRenderer er;
		JComboBox x1Combo = new JComboBox(ALIGNMENT_OPTS);
		JComboBox x2Combo = new JComboBox(ALIGNMENT_OPTS);
		JComboBox y1Combo = new JComboBox(ALIGNMENT_OPTS);
		JComboBox y2Combo = new JComboBox(ALIGNMENT_OPTS);{
			ActionListener listener = new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					if (er == null) {
						//do nothing
					}
					else if(e.getSource() == x1Combo){
						er.setHorizontalAlignment1(
								x1Combo.getSelectedIndex());
					}
					else if (e.getSource() == x2Combo){
						er.setHorizontalAlignment2(
								x2Combo.getSelectedIndex());
					}
					else if (e.getSource() == y1Combo){
						er.setVerticalAlignment1(
								y1Combo.getSelectedIndex());
					}
					else{
						er.setVerticalAlignment2(
								y2Combo.getSelectedIndex());
					}
					visRepaint(vis);
				}
			};
			x1Combo.addActionListener(listener);
			x2Combo.addActionListener(listener);
			y1Combo.addActionListener(listener);
			y2Combo.addActionListener(listener);
		}
		JComboBox arrowCombo = new JComboBox();{
			arrowCombo.addItem("NO ARROW");
			arrowCombo.addItem("ARROW FORWARD");
			arrowCombo.addItem("ARROW REVERSE");
			arrowCombo.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					if (er!=null){
						er.setArrowType(arrowCombo.getSelectedIndex());
						visRepaint(vis);
					}
				}
			});
		}
		JComboBox edgeCombo = new JComboBox();{
			edgeCombo.addItem("LINE");
			edgeCombo.addItem("CURVE");
			edgeCombo.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					if (er!=null){
						er.setEdgeType(edgeCombo.getSelectedIndex());
						visRepaint(vis);
					}
				}
			});
		}
		JSpinner wSpin = new JSpinner();{
			wSpin.addChangeListener(new ChangeListener() {
				public void stateChanged(ChangeEvent arg0) {
					if (er!=null) {
						er.setArrowHeadSize(
								((Number)wSpin.getValue()).intValue(),
								er.getArrowHeadHeight());
						visRepaint(vis);
					}
				}
			});
		}
		JSpinner hSpin = new JSpinner();{
			hSpin.addChangeListener(new ChangeListener() {
				public void stateChanged(ChangeEvent e) {
					if (er!=null) {
						er.setArrowHeadSize(er.getArrowHeadWidth(), 
								((Number)hSpin.getValue()).intValue());
						visRepaint(vis);
					}
				}
			});
		}
		JFormattedTextField wField = decimalField();{
			wField.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					if (er!=null) {
						er.setDefaultLineWidth(
								Double.parseDouble(wField.getText()));
						visRepaint(vis);
					}
				}
			});
		}
		LabeledBox box = new LabeledBox("Edge");{
			box.addComp("Edge type:", edgeCombo);
			box.addComp("Arrow type:", arrowCombo);
			box.addComp("X1 alignment:", x1Combo);
			box.addComp("Y1 alignment:", y1Combo);
			box.addComp("X2 alginment:", x2Combo);
			box.addComp("Y2 alginment:", y2Combo);
			box.addComp("Head width:", wSpin);
			box.addComp("Head height:", hSpin);
			box.addComp("Line width:", wField);
		}
		public Box getSettingBox(Visualization vis, Renderer ren) {
			this.vis = null;
			er = null;
			EdgeRenderer tr = (EdgeRenderer) ren;
			edgeCombo.setSelectedIndex(tr.getEdgeType());
			arrowCombo.setSelectedIndex(tr.getArrowType());
			x1Combo.setSelectedIndex(tr.getHorizontalAlignment1());
			x2Combo.setSelectedIndex(tr.getHorizontalAlignment2());
			y1Combo.setSelectedIndex(tr.getVerticalAlignment1());
			y2Combo.setSelectedIndex(tr.getVerticalAlignment2());
			wSpin.setValue(tr.getArrowHeadWidth());
			hSpin.setValue(tr.getArrowHeadHeight());
			wField.setText(tr.getDefaultLineWidth()+"");
			this.vis = vis;
			this.er = (EdgeRenderer) ren;
			return box;
		}
	}
	public static class LabelSetting implements Setting{
		Visualization vis;
		LabelRenderer lr;

		JComboBox xAli = new JComboBox(ALIGNMENT_OPTS);
		JComboBox yAli = new JComboBox(ALIGNMENT_OPTS);
		JComboBox hTAli = new JComboBox(ALIGNMENT_OPTS);
		JComboBox vTAli = new JComboBox(ALIGNMENT_OPTS);
		JComboBox hIAli = new JComboBox(ALIGNMENT_OPTS);
		JComboBox vIAli = new JComboBox(ALIGNMENT_OPTS);
		JComboBox iPos = new JComboBox(ALIGNMENT_OPTS);{
			ActionListener l = new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					if (lr == null) {
						//do nothing
					}
					else if (e.getSource() == xAli){
						lr.setHorizontalAlignment(
								xAli.getSelectedIndex());
					}
					else if (e.getSource() == yAli){
						lr.setVerticalAlignment(
								yAli.getSelectedIndex());
					}
					else if (e.getSource() == hTAli){
						lr.setHorizontalTextAlignment(
								hTAli.getSelectedIndex());
					}
					else if (e.getSource() == vTAli){
						lr.setVerticalTextAlignment(
								vTAli.getSelectedIndex());
					}
					else if (e.getSource() == hIAli){
						lr.setHorizontalTextAlignment(
								hIAli.getSelectedIndex());
					}
					else if (e.getSource() == vIAli){
						lr.setVerticalImageAlignment(
								vIAli.getSelectedIndex());
					}
					else {
						lr.setImagePosition(iPos.getSelectedIndex());
					}
					visRepaint(vis);
				}
			};
			xAli.addActionListener(l);
			yAli.addActionListener(l);
			hTAli.addActionListener(l);
			vTAli.addActionListener(l);
			hIAli.addActionListener(l);
			vIAli.addActionListener(l);
		}
		JTextField labelField = new JTextField();{
			labelField.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					if (lr != null) {
						if (labelField.getText().length() == 0){
							lr.setTextField(null);
						}
						else {
							lr.setTextField(labelField.getText());	
						}
						visRepaint(vis);
					}
				}
			});
		}
		JTextField imageField = new JTextField();{
			imageField.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					if (lr != null) {
						if (imageField.getText().length() == 0) {
							lr.setImageField(null);
						}
						else {
							lr.setImageField(imageField.getText());
						}
						visRepaint(vis);
					}
				}
			});
		}
		JSpinner hBorder = new JSpinner();{
			hBorder.addChangeListener(new ChangeListener() {
				public void stateChanged(ChangeEvent e) {
					if (lr != null) {
						int val = ((Number)hBorder.getValue()).intValue();
						lr.setHorizontalPadding(val);
						visRepaint(vis);
					}
				}
			});
		
		}
		JSpinner vBorder = new JSpinner();{
			vBorder.addChangeListener(new ChangeListener() {
				public void stateChanged(ChangeEvent e) {
					if (lr != null) {
						int val = ((Number)vBorder.getValue()).intValue();
						lr.setVerticalPadding(val);
						visRepaint(vis);
					}
				}
			});
		
		}
		JSpinner iMargin = new JSpinner();{
			iMargin.addChangeListener(new ChangeListener() {
				public void stateChanged(ChangeEvent e) {
					if (lr != null) {
						int val = ((Number)iMargin.getValue()).intValue();
						lr.setImageTextPadding(val);
						visRepaint(vis);
					}
				}
			});
		
		}
		JSpinner arcW = new JSpinner();
		JSpinner arcH = new JSpinner();{
			ChangeListener l = new ChangeListener() {
				public void stateChanged(ChangeEvent arg0) {
					if (lr != null) {
						int w = ((Number)arcW.getValue()).intValue();
						int h = ((Number)arcH.getValue()).intValue();
						lr.setRoundedCorner(w, h);
						visRepaint(vis);
					}
				}
			};
			arcW.addChangeListener(l);
			arcH.addChangeListener(l);
		}
		LabeledBox box = new LabeledBox("Label");{
			box.addComp("Node X alignment:", xAli);
			box.addComp("Node Y alignment:", yAli);
			box.addComp("Text X alignment:", hTAli);
			box.addComp("Text Y alignment:", vTAli);
			box.addComp("Img X alginment:", hIAli);
			box.addComp("Img Y alginment:", vIAli);
			box.addComp("Img position:", iPos);
			box.addComp("Label field:", labelField);
			box.addComp("Image field:", imageField);
			box.addComp("X padding:", hBorder);
			box.addComp("Y padding:", vBorder);
			box.addComp("Image margin:", iMargin);
			box.addComp("Corner width:", arcW);
			box.addComp("Corner height:", arcH);
		}
		public Box getSettingBox(Visualization vis, Renderer ren) {
			this.vis = null;
			this.lr = null;
			LabelRenderer tr = (LabelRenderer) ren;
			xAli.setSelectedIndex(tr.getHorizontalAlignment());
			yAli.setSelectedIndex(tr.getVerticalAlignment());
			hTAli.setSelectedIndex(tr.getHorizontalTextAlignment());
			vTAli.setSelectedIndex(tr.getVerticalTextAlignment());
			hIAli.setSelectedIndex(tr.getHorizontalImageAlignment());
			vIAli.setSelectedIndex(tr.getVerticalImageAlignment());
			iPos.setSelectedIndex(tr.getImagePosition());
			labelField.setText(tr.getTextField());
			imageField.setText(tr.getImageField());
			hBorder.setValue(tr.getHorizontalPadding());
			vBorder.setValue(tr.getVerticalPadding());
			iMargin.setValue(tr.getImageTextPadding());
			Object rec = fieldObj("m_bbox", tr);
			if (rec instanceof RoundRectangle2D) {
				RoundRectangle2D rr = (RoundRectangle2D) rec;
				arcW.setValue((int)rr.getArcWidth());
				arcH.setValue((int)rr.getArcHeight());
			}
			else{
				arcW.setValue(0);
				arcH.setValue(0);
			}
			this.vis = vis;
			this.lr = (LabelRenderer) ren;
			return box;
		}
	}
	private static Map<Class, Class> SETTING_MAP = new LinkedHashMap();
	static {
		SETTING_MAP.put(AbstractShapeRenderer.class, AbstractShapeSetting.class);
		SETTING_MAP.put(AxisRenderer.class,          AxisSetting.class);
		SETTING_MAP.put(ShapeRenderer.class,         ShapeSetting.class);
		SETTING_MAP.put(PolygonRenderer.class,       PolygonSetting.class);
		SETTING_MAP.put(EdgeRenderer.class,          EdgeSetting.class);
		SETTING_MAP.put(LabelRenderer.class,         LabelSetting.class);
	}
	private Map<Class, Setting> settingMap = new LinkedHashMap();{
		settingMap.put(AbstractShapeRenderer.class, new AbstractShapeSetting());
		settingMap.put(AxisRenderer.class,          new AxisSetting());
		settingMap.put(ShapeRenderer.class,         new ShapeSetting());
		settingMap.put(PolygonRenderer.class,       new PolygonSetting());
		settingMap.put(EdgeRenderer.class,          new EdgeSetting());
		settingMap.put(LabelRenderer.class,         new LabelSetting());
	}
	public static JFormattedTextField decimalField(){
		return new JFormattedTextField(new DecimalFormat());
	}
	
	//info prefix
	protected static final String PREFIX = " Rendering group(s): ";
	//the label to show the info
	protected JLabel groupInfoLabel = new JLabel(PREFIX);{
		groupInfoLabel.setEnabled(false);
		groupInfoLabel.setAlignmentX(0);
	}
	protected JComboBox rendererCombo = new JComboBox();{
		rendererCombo.setAlignmentX(0);
	}
	//when mouse entering the combobox, we re-collect everything, so we do
	//not have to add listeners to anything to make sure this combobox is
	//always up-to-date
	protected MouseAdapter preLoadAction = new MouseAdapter() {
		@Override
		public void mouseEntered(MouseEvent e) {
			Object selected = rendererCombo.getSelectedItem();
			rendererCombo.removeActionListener(actionListener);
			rendererCombo.removeAllItems();
			
			rendererCombo.addItem(NO_RENDERER);
			
			List<Renderer> rens = getRenderers();
			for (Renderer renderer : rens) {
				rendererCombo.addItem(renderer);
				if (selected == renderer) {
					rendererCombo.setSelectedItem(renderer);
				}
			}
			rendererCombo.addActionListener(actionListener);
		}
	};
	protected ActionListener actionListener = new ActionListener() {
		public void actionPerformed(ActionEvent e) {
			for(int i = getComponentCount()-1; i>=0; i--){
				//we remove comps backwards, until we hit the groupinfolabel
				if (getComponent(i)!=groupInfoLabel) {
					remove(getComponent(i));
				}
				else{
					break;
				}
			}
			if (rendererCombo.getSelectedItem().equals(NO_RENDERER)) {
				((JComponent)getParent()).updateUI();
				groupInfoLabel.setText(PREFIX+"[]");
				return;
			}
			Renderer ren = (Renderer) rendererCombo.getSelectedItem();
			Box component = null;
			for(Class type : settingMap.keySet()){
				if (type.isAssignableFrom(ren.getClass())) {
					component = settingMap.get(type).getSettingBox(vis,ren);
					add(component);
				}
			}
			if (ren instanceof Setting) {
				component = ((Setting)ren).getSettingBox(vis, ren);
				add(component);
			}
			List<String> list = getRendererGroups(ren);
			groupInfoLabel.setText(PREFIX+list.toString());
			if (component == null) {
				String name = ren.getClass().getName();
				name = name.substring(name.lastIndexOf(".")+1);
				add(new JLabel(" No setting box for "+name));
				Debug.info("No setting box for "+ren.getClass());
			}
			((JComponent)getParent()).updateUI();
		}
	};
	public static Box createSettingBox(String title, Visualization vis, Renderer ren){
		Box result = Box.createVerticalBox();
		result.setBorder(new TitledBorder(title));
		Box component = null;
		for(Class type : SETTING_MAP.keySet()){
			if (type.isAssignableFrom(ren.getClass())) {
				Setting setting = null;
				try {
					setting = (Setting) SETTING_MAP.get(type).newInstance();
				} catch (InstantiationException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
				component = setting.getSettingBox(vis,ren);
				result.add(component);
			}
		}
		if (ren instanceof Setting) {
			component = ((Setting)ren).getSettingBox(vis, ren);
			result.add(component);
		}
		return result;
	}
	protected List<Renderer> getRenderers(){
		List<Renderer> result = new ArrayList<Renderer>();
		for (PredicateChain predicateChain : chains){
			result.addAll(Pre.getRenders(predicateChain));
		}
		return result;
	}

	protected List<String> getRendererGroups(Renderer renderer){
		List<String> groups = new ArrayList<String>();
		for (PredicateChain predicateChain : chains){
			groups.addAll(Pre.getFocusGroups(predicateChain, renderer));
		}
		return groups;
	}
	protected List<PredicateChain> chains = new ArrayList<PredicateChain>();
	protected PredicateChain looseRenderersChain = new PredicateChain();
	protected Visualization vis = null;
	
	/**
	 * Please set the visualization, becasuse we need it to update the display
	 * when the renderer settings are changed.
	 */
	public void setVisualization(Visualization vis){
		this.vis = vis;
	}
	/**
	 * Add a renderer control chain to this box. Please notice that all objects
	 * in this chain should be renderers, otherwise, you will get an exception.
	 * @see Pre#getRenders(PredicateChain)
	 */
	public void addChain(PredicateChain chain){
		chains.add(chain);
	}
	/**
	 * Add a single renderer to this box. The group information is displayed
	 * in the {@link #groupInfoLabel} when the renderer is selected.
	 */
	public void addRenderer(Renderer renderer, String... group){
		if (group == null) {
			addRenderer(renderer);
		}
		for (String string : group) {
			looseRenderersChain.add(new InGroupPredicate(string), renderer);
		}
		if (!chains.contains(looseRenderersChain)) {
			chains.add(looseRenderersChain);
		}
	}
	/**
	 * Add a single renderer to this box. 
	 */
	public void addRenderer(Renderer renderer){
		addRenderer(renderer, " ");
	}
	public RendererBox(Visualization vis, PredicateChain... chains) {
		super(BoxLayout.Y_AXIS);
		if (chains != null) {
			for (PredicateChain predicateChain : chains) {
				this.chains.add(predicateChain);
			}
		}
		this.vis = vis;
		//load the items for the first time by directly calling the action
		preLoadAction.mouseEntered(null);
		rendererCombo.getComponent(0).addMouseMotionListener(preLoadAction);
		rendererCombo.getComponent(0).addMouseListener(preLoadAction);
		rendererCombo.setPreferredSize(new Dimension(100, 30));
		rendererCombo.setMinimumSize(rendererCombo.getPreferredSize());
		this.add(rendererCombo);
		this.add(groupInfoLabel);
		this.setBorder(new TitledBorder("Renderer Setting"));
	}
	
	public void setTitle(String title){
		((TitledBorder)getBorder()).setTitle(title);
	}
}